using System;
using System.Collections.Generic;
using System.Collections;
using System.IO;
using System.Diagnostics;

/*
::---------------------------------
:: ArchiveBytes v1.0 Windows.CS-Script v1.0
:: Copyright 2008 Mark T. Ward
::
:: Licensed under the Apache License, Version 2.0 (the "License");
:: you may not use this file except in compliance with the License.
:: You may obtain a copy of the License at
::
::     http://www.apache.org/licenses/LICENSE-2.0
::
:: Unless required by applicable law or agreed to in writing, software
:: distributed under the License is distributed on an "AS IS" BASIS,
:: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
:: See the License for the specific language governing permissions and
:: limitations under the License.
::---------------------------------
 */

/// <summary>
/// 
/// </summary>
class Script
{
    /// <summary>
    /// 
    /// </summary>
    /// <param name="args"></param>
    [STAThread]
    static public void Main(string[] args)
    {
        /*
         * Archive repository configuration
         * 
         * The first Archive Repository in the list is the repository used as the default
         * repository.  The first repository entry is used to create the repository.  Then
         * its contents is copied to the remainder of the repositories.
         */
        List<ArchiveRepositoryDefinition> ArchiveRepositories = new List<ArchiveRepositoryDefinition>() 
                { 
                    new ArchiveRepositoryDefinition("ArchiveExampleOne",2), 
                    new ArchiveRepositoryDefinition("ArchiveExampleTwo",3)
                };
        
        /*
         * Create the ArchiveBytes class with the repository reference
         */
        ArchiveBytes archiveBytes = new ArchiveBytes(ArchiveRepositories);

        /*
         * Initialize archiveBytes with the list of data methods to archive
         */
        archiveBytes.ArchiveDataJob = new ArchiveData(ArchiveExampleOneData);
        archiveBytes.ArchiveDataJob += new ArchiveData(ArchiveExampleTwoData);

        /*
         * Execute the archive
         */
        archiveBytes.Run();
    }

    /// <summary>
    /// A data method to archive data into the target path.  This is user generated code.
    /// </summary>
    /// <param name="TargetPath"></param>
    static private void ArchiveExampleOneData(string TargetPath)
    {
        /*
         * Create the archive data example in the target path
         */
        File.WriteAllText(Path.Combine(TargetPath, "ArchiveData.txt"), "hello world " + DateTime.Now.ToString());
    }

    static private void ArchiveExampleTwoData(string TargetPath)
    {
        /*
         * Create the archive data example in the target path
         */
        File.WriteAllText(Path.Combine(TargetPath, "ArchiveDataTwo.txt"), "hello world " + DateTime.Now.ToString());
    }


    //---------------------------------------------------------------------------------\\
    //---------------------------------------------------------------------------------\\

    #region Helper Methods
    /// <summary>
    /// 
    /// </summary>
    /// <param name="SourcePath"></param>
    /// <param name="TargetPath"></param>
    /// <param name="ZipTitle"></param>
    /// <param name="VolumeSize">In megabytes</param>
    static private void Zip(string SourcePath, string TargetPath, string ZipTitle, int VolumeSize)
    {
        /*
7za a "g:\Backup\00-Day\MyDocuments.7zip" -y -v650m -r "d:\Documents and Settings\zenoza\My Documents\*" 
7za t "g:\Backup\00-Day\MyDocuments.7zip.001" > "g:\Backup\00-Day\MyDocuments.Test.log.txt"
       */

        string ZipFileName = Path.Combine(TargetPath, Environment.MachineName + "." + ZipTitle + ".7zip");
        Process myProcess = new Process();
        myProcess.StartInfo.FileName = "7za";
        myProcess.StartInfo.Arguments = " a \"" + ZipFileName + "\" -y -v" + VolumeSize.ToString() + "m -r \"" + SourcePath + "\"";
        myProcess.StartInfo.UseShellExecute = false;
        myProcess.StartInfo.RedirectStandardOutput = true;
        myProcess.StartInfo.CreateNoWindow = true;
        myProcess.Start();

        string line = null;
        StreamWriter zipLogFile = File.CreateText(Path.Combine(TargetPath, Environment.MachineName + "." + ZipTitle + ".ZIP.Log.txt"));
        using (zipLogFile)
        {
            while (null != (line = myProcess.StandardOutput.ReadLine()))
            {
                Console.WriteLine(line);
                zipLogFile.WriteLine(line);
            }
            myProcess.WaitForExit();
        }
        zipLogFile.Close();

        // Test the file
        myProcess = new Process();
        myProcess.StartInfo.FileName = "7za";
        myProcess.StartInfo.Arguments = " t \"" + ZipFileName + ".001\"";
        myProcess.StartInfo.UseShellExecute = false;
        myProcess.StartInfo.RedirectStandardOutput = true;
        myProcess.StartInfo.CreateNoWindow = true;
        myProcess.Start();

        line = null;
        zipLogFile = File.CreateText(Path.Combine(TargetPath, Environment.MachineName + "." + ZipTitle + ".ZIP.Test.Log.txt"));
        using (zipLogFile)
        {
            while (null != (line = myProcess.StandardOutput.ReadLine()))
            {
                Console.WriteLine(line);
                zipLogFile.WriteLine(line);
            }
            myProcess.WaitForExit();
        }
        zipLogFile.Close();

    }
    #endregion //Helper Methods

    //---------------------------------------------------------------------------------\\
    //---------------------------------------------------------------------------------\\
    //---------------------------------------------------------------------------------\\
    //---------------------------------------------------------------------------------\\

    #region ArchiveBytes v 1.0
    /// <summary>
    /// 
    /// </summary>
    /// <param name="TargetPath">The path the archive data method should copy its data.</param>
    delegate void ArchiveData(string TargetPath);

    /// <summary>
    /// 
    /// </summary>
    class ArchiveBytes
    {
        /// <summary>
        /// 
        /// </summary>
        public ArchiveData ArchiveDataJob;

        private List<ArchiveRepositoryDefinition> ArchiveRepositories;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="ArchiveRepositories"></param>
        public ArchiveBytes(List<ArchiveRepositoryDefinition> ArchiveRepositories)
        {
            this.ArchiveRepositories = ArchiveRepositories;
        }

        /// <summary>
        /// 
        /// </summary>
        public void Run()
        {
            /*
         * Initialize resources
         * 
         * To initialize resources for archiving edit the InitializeRecources method.  Be sure to release resources in 
         * the ReleaseResources method.
         */
            InitializeResources();

            /*
             * Initialize Repositories
             */
            InitializeRepositories(ArchiveRepositories);

            /*
             * Process archive solutions
             */
            ArchiveDataJob(Path.Combine(ArchiveRepositories[0].RepositoryPath, "Archive\\00-Archive\\Data"));

            /*
             * Create hash of the first archive for data validation
             */
            GenerateHash(ArchiveRepositories[0]);

            /* 
             * Copy repository one's data to the rest of the repositories and verify data
             */
            CopyRepository(ArchiveRepositories);

            /*
             * Verify Repositories
             */
            ValidateRepositories(ArchiveRepositories);

            /* 
             * Release locks
             */
            ReleaseLocks(ArchiveRepositories);

            /*
             * Release resources
             */
            ReleaseResources();

        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="archiveRepositoryDefinition"></param>
        private void CopyRepository(List<ArchiveRepositoryDefinition> ArchiveRepositories)
        {
            // [example] xcopy /E /Y "d:\ArchiveRepositoryPath\Archive\00-Archive\*.*" "q:\ADifferentArchiveRepositoryPath\Archive\00-Archive"

            // rather than building my own xcopy.... use xcopy!!
            string ArchiveSourcePath = Path.Combine(ArchiveRepositories[0].RepositoryPath, "Archive\\00-Archive");


            for (int i = 1; i < ArchiveRepositories.Count; i++)
            {
                string ArchiveRepositoryPath = Path.Combine(ArchiveRepositories[i].RepositoryPath, "Archive\\00-Archive");

                Process myProcess = new Process();
                myProcess.StartInfo.FileName = "xcopy";
                myProcess.StartInfo.Arguments = "/E /Y " + "\"" + ArchiveSourcePath + "\\*.*\"" + " \"" + ArchiveRepositoryPath + "\"";
                myProcess.StartInfo.UseShellExecute = false;
                myProcess.StartInfo.RedirectStandardOutput = true;
                myProcess.StartInfo.CreateNoWindow = true;
                myProcess.Start();
                string line = null;
                while (null != (line = myProcess.StandardOutput.ReadLine()))
                {
                    Console.WriteLine(line);
                }
                myProcess.WaitForExit();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        private void InitializeResources()
        {
            /*
                ::------------------------------
                :: [optiona] Mount network drives
                ::------------------------------
                ::[example] net use b: \\ComputerName\ShareName /user:UserName Password
             */
        }

        /// <summary>
        /// 
        /// </summary>
        private void ReleaseResources()
        {
            /*
                ::------------------------------
                :: [optiona] Unmount network drives
                ::------------------------------
                ::net use /delete b:
             */
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="ArchiveRepository"></param>
        private void GenerateHash(ArchiveRepositoryDefinition ArchiveRepository)
        {
            string ArchiveRepositoryPath = Path.Combine(ArchiveRepository.RepositoryPath, "Archive");
            string ArchiveRepositoryTargetPath = Path.Combine(ArchiveRepositoryPath, "00-Archive\\Data");
            string ArchiveChecksumPath = Path.Combine(ArchiveRepositoryPath, "00-Archive");

            Process myProcess = new Process();
            myProcess.StartInfo.FileName = "fciv.exe";
            myProcess.StartInfo.Arguments = "\"" + ArchiveRepositoryTargetPath + "\"" + " -sha1 -wp -xml " + "\"" + Path.Combine(ArchiveChecksumPath, Environment.MachineName + ".checksum.xml") + "\"";
            myProcess.StartInfo.UseShellExecute = false;
            myProcess.StartInfo.RedirectStandardOutput = true;
            myProcess.StartInfo.CreateNoWindow = true;
            myProcess.Start();
            string line = null;
            while (null != (line = myProcess.StandardOutput.ReadLine()))
            {
                Console.WriteLine(line);
            }
            myProcess.WaitForExit();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="ArchiveRepositories"></param>
        private void ValidateRepositories(List<ArchiveRepositoryDefinition> ArchiveRepositories)
        {
            foreach (ArchiveRepositoryDefinition ArchiveRepository in ArchiveRepositories)
            {
                string ArchiveRepositoryPath = Path.Combine(ArchiveRepository.RepositoryPath, "Archive");
                string ArchiveRepositoryTargetPath = Path.Combine(ArchiveRepositoryPath, "00-Archive\\Data");
                string ArchiveChecksumPath = Path.Combine(ArchiveRepositoryPath, "00-Archive");

                Process myProcess = new Process();
                myProcess.StartInfo.FileName = "fciv.exe";
                myProcess.StartInfo.Arguments = "-v -bp \"" + ArchiveRepositoryTargetPath + "\"" + " -sha1 -wp -xml " + "\"" + Path.Combine(ArchiveChecksumPath, Environment.MachineName + ".checksum.xml") + "\"";
                myProcess.StartInfo.UseShellExecute = false;
                myProcess.StartInfo.RedirectStandardOutput = true;
                myProcess.StartInfo.CreateNoWindow = true;
                myProcess.Start();
                
                string line = null;
                StreamWriter verifyFile = File.CreateText(Path.Combine(ArchiveChecksumPath, Environment.MachineName + ".checksum.verify.txt"));
                using (verifyFile)
                {
                    while (null != (line = myProcess.StandardOutput.ReadLine()))
                    {
                        Console.WriteLine(line);
                        verifyFile.WriteLine(line);
                    }
                    myProcess.WaitForExit();
                }
                verifyFile.Close();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        private void ReleaseLocks(List<ArchiveRepositoryDefinition> ArchiveRepositories)
        {
            foreach (ArchiveRepositoryDefinition archiveDefinition in ArchiveRepositories)
            {
                string ArchiveRepositoryPath = Path.Combine(archiveDefinition.RepositoryPath, "Archive");

                // Delete archive entry lock
                string ArchiveEntryLock = Path.Combine(ArchiveRepositoryPath, "00-Archive\\" + Environment.MachineName + ".lock");
                if (File.Exists(ArchiveEntryLock))
                {
                    File.Delete(ArchiveEntryLock);
                }

                // Delete archive lock
                string ArchiveLock = Path.Combine(ArchiveRepositoryPath, Environment.MachineName + ".lock");
                if (File.Exists(ArchiveLock))
                {
                    File.Delete(ArchiveLock);
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="ArchiveRepositories"></param>
        private void InitializeRepositories(List<ArchiveRepositoryDefinition> ArchiveRepositories)
        {
            /*
             * Initialize each repository in the ArchiveRepository list
             */
            foreach (ArchiveRepositoryDefinition archiveDefinition in ArchiveRepositories)
            {
                string ArchiveRepositoryPath = Path.Combine(archiveDefinition.RepositoryPath, "Archive");
                // Create Archive repository in each archivedefinition path
                Directory.CreateDirectory(ArchiveRepositoryPath);

                // Create Archive lock file
                File.WriteAllText(Path.Combine(ArchiveRepositoryPath, Environment.MachineName + ".lock"), "Archive lock " + DateTime.Now.ToString());

                // Remove the oldest archive entry in the archive repository definition
                string oldestPath = (archiveDefinition.ArchiveCount - 1).ToString().PadLeft(2, '0') + "-Archive";
                oldestPath = Path.Combine(ArchiveRepositoryPath, oldestPath);
                if (Directory.Exists(oldestPath))
                {
                    Directory.Delete(oldestPath, true);
                }

                // Shuffle archive repository entries
                if (archiveDefinition.ArchiveCount - 2 >= 0)
                {
                    for (int i = archiveDefinition.ArchiveCount - 2; i >= 0; i--)
                    {
                        // move current repository entry + 1 in the archive entry index
                        string archiveSourceEntryPath = i.ToString().PadLeft(2, '0') + "-Archive";
                        archiveSourceEntryPath = Path.Combine(ArchiveRepositoryPath, archiveSourceEntryPath);

                        string archiveTargetEntryPath = (i + 1).ToString().PadLeft(2, '0') + "-Archive";
                        archiveTargetEntryPath = Path.Combine(ArchiveRepositoryPath, archiveTargetEntryPath);


                        if (Directory.Exists(archiveSourceEntryPath))
                        {
                            Directory.Move(archiveSourceEntryPath, archiveTargetEntryPath);
                        }
                    }
                }

                // Create the repository 00 entry
                Directory.CreateDirectory(Path.Combine(ArchiveRepositoryPath, "00-Archive\\Data"));

                // Create the repository lock
                File.WriteAllText(Path.Combine(ArchiveRepositoryPath, "00-Archive\\" + Environment.MachineName + ".lock"), "Archive lock " + DateTime.Now.ToString());
            }
        }
    }

    /// <summary>
    /// 
    /// </summary>
    private class ArchiveRepositoryDefinition
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="FolderPath"></param>
        /// <param name="Count"></param>
        public ArchiveRepositoryDefinition(string FolderPath, int Count)
        {
            RepositoryPath = FolderPath;
            ArchiveCount = Count;
        }

        /// <summary>
        /// 
        /// </summary>
        public string RepositoryPath;

        /// <summary>
        /// 
        /// </summary>
        public int ArchiveCount;

    }
    #endregion // Archive Bytes 
}

